/**    
 * 文件名：AbstractPaymentResponseHandler.java</br>
 *    
 * 版本信息：v1.0</br>
 * 日期：2017年7月7日</br>
 * © 2005-2017 雷技信息科技（上海）有限公司版权所有</br>
 *
 */
package com.logic.landseaserver.models.payment.service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.logic.common.security.SecurityUtils;
import com.logic.landseaserver.common.exception.LandseaException;
import com.logic.landseaserver.common.exception.ResultCodes.PaymentResultCode;
import com.logic.landseaserver.common.util.StringTools;
import com.logic.landseaserver.domain.BillPayment;
import com.logic.landseaserver.models.payment.BillPaymentCollections;
import com.logic.landseaserver.models.payment.PaymentConstant;
import com.logic.landseaserver.models.payment.bean.PaymentHelperBean;
import com.logic.landseaserver.models.payment.bean.PaymentRequest;
import com.logic.landseaserver.models.payment.channel.AbstractPaymentChannel;
import com.logic.landseaserver.models.payment.impl.RechargePayResponseHandler;
import com.logic.landseaserver.models.prepayment.bean.AotuPayBill;
import com.logic.landseaserver.models.prepayment.service.AutoPayBillService;
import com.logic.landseaserver.persistence.write.BillPaymentWriteMapper;
import com.logic.system.AppContext;

/**
 *
 * 项目名称：system-server</br>
 * 类名称：AbstractPaymentResponseHandler</br>
 * 类描述：支付回调</br>
 * 创建人：Aaron</br>
 * 创建时间：2017年7月7日 下午4:37:28</br>
 * 
 * @version 1.0
 *
 */
public abstract class AbstractPaymentResponseHandler
{
    protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractPaymentResponseHandler.class);
    
    @Autowired
    private AppContext appContext;
    
    @Autowired
    private BillPaymentWriteMapper billPaymentWriteMapper;
    
    @Autowired
    private AutoPayBillService autoPayBillService;
    
    /**
     * 
     * [简要描述]：支付</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-07)</br>
     *
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {LandseaException.class, Exception.class})
    public Map<String, Object> doResponse(BillPayment billPayment, Object paymentResponse)
        throws LandseaException
    {
        String logInfo = getLog(billPayment, null);
        LOGGER.info(logInfo + " Start|Response|" + paymentResponse.toString());
        
        // 自定义中间传递对象，用来承载每一步处理的输出
        PaymentHelperBean helperObject = new PaymentHelperBean();
        LOGGER.info(logInfo + " Build response helper object has been finished.");
        
        // 返回结果
        Map<String, Object> resultMap = new HashMap<String, Object>();
        
        // 解析通知结果,根据结果构造billPayment,如果流程需要结束，就直接返回
        // 根据支付结果，判断流程走向
        boolean isNeedContinue = analysisResult(billPayment, paymentResponse, helperObject, resultMap);
        
        if (!isNeedContinue)
        {
            LOGGER.info(logInfo + "处理流程终止");
            return resultMap;
        }
        
        LOGGER.info(logInfo + " Analysis payment result has been finished.");
        
        // 开始处理业务,能走到这里，说明支付是成功的，需要更新本地的各种数据
        LOGGER.info(logInfo + " Start process response service.");
        doBusiness(billPayment, helperObject, resultMap);
        
        return resultMap;
    }
    
    /**
     * 
     * [简要描述]：开始处理业务</br>
     * [详细描述]：事务已添加，子类无需添加事务注解</br>
     * [作者]：Aaron(2017-07-07)</br>
     *
     * @param payment 支付对象
     * @param helperObject 帮助对象
     * @throws LandseaException 异常
     *
     */
    public void doBusiness(BillPayment billPayment, PaymentHelperBean helperObject, Map<String, Object> resultMap)
        throws LandseaException
    {
        String logInfo = getLog(billPayment, null);
        try
        {
            // 公共业务处理
            doCommonBusiness(billPayment, resultMap);
            
            LOGGER.info(logInfo + "| do common busniess finished.");
            
            // 本地业务处理
            doLocalBusiness(billPayment, helperObject, resultMap);
            
            // 不同的支付渠道，返回的结果不同
            String beanId = PaymentConstant.GENETRATE_BILLPAYMENT_PREFIX +   BillPaymentCollections.getPayChannelByPayType(billPayment.getPayType());
            AbstractPaymentChannel paymentChannel = (AbstractPaymentChannel)appContext.getBean(beanId);
            
            if (null == paymentChannel)
            {
                LOGGER.error(logInfo + "|get paymentChannel failed,beanId=" + beanId);
                throw LandseaException.createException(PaymentResultCode.E00150006);
            }
            paymentChannel.buildResponseResult(billPayment, helperObject, resultMap);
            
            LOGGER.info(logInfo + "| Local pay response service has been finished, [ paymentId=" + billPayment.getId() + "].");
        }
        catch (LandseaException e)
        {
            // 自定义异常直接抛出
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.info(logInfo + "| Do pay response business failed.", e);
            throw LandseaException.createException(PaymentResultCode.E00150007, e);
        }
    }
    
    /**
     * 
     * [简要描述]：公共业务</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-19)</br>
     *
     * @param billPayment
     * @param helperObject
     * @param resultMap
     *
     */
    private void doCommonBusiness(BillPayment billPayment, Map<String, Object> resultMap)
    {
        // 更新支付表-只更新状态、时间、支付结果、说明、公共字段、成功时的订单号
        BillPayment paymentUpdate = new BillPayment();
        paymentUpdate.setId(billPayment.getId());
        paymentUpdate.setStatus(billPayment.getStatus());
        paymentUpdate.setTime(new Date());
        paymentUpdate.setComments(billPayment.getComments());
        paymentUpdate.setTransactionNo(billPayment.getTransactionNo());
        
        // 添加公共字段
        paymentUpdate.updateCommonInfo(SecurityUtils.getCurrentLogin().getUserId());
        
        billPayment.setTime(paymentUpdate.getTime());
        billPaymentWriteMapper.updateByPrimaryKeySelective(paymentUpdate);
    }
    
    /**
     * 
     * [简要描述]：获取支付日志打印前缀</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-12)</br>
     *
     * @return log info
     *
     */
    protected String getLog(BillPayment payment, Integer paymentId)
    {
        String billTypeName = BillPaymentCollections.getBillTypeName(payment.getBillType());
        String payType = payment.getPayType();
        String messageId = getMessageId(payType, String.valueOf(payment.getBillId()), paymentId);
        
        // 规则：账单类型|支付方式|处理类名称|消息Id|
        StringBuilder logStringBuilder = new StringBuilder();
        logStringBuilder.append(billTypeName);
        logStringBuilder.append(payType);
        logStringBuilder.append("|");
        logStringBuilder.append(getProcessClassName());
        logStringBuilder.append("|");
        logStringBuilder.append(messageId);
        logStringBuilder.append("|");
        
        return logStringBuilder.toString();
    }
    
    /**
     * 
     * [简要描述]：获取支付日志打印前缀</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-12)</br>
     *
     * @return log info
     *
     */
    protected String getLog(PaymentRequest payment, Integer paymentId)
    {
        String billTypeName = BillPaymentCollections.getBillTypeName(payment.getBillType());
        String payType = payment.getPayType();
        String messageId = getMessageId(payType, String.valueOf(payment.getBillId()), paymentId);
        
        // 规则：账单类型|支付方式|处理类名称|消息Id|
        StringBuilder logStringBuilder = new StringBuilder();
        logStringBuilder.append(billTypeName);
        logStringBuilder.append(payType);
        logStringBuilder.append("|");
        logStringBuilder.append(getProcessClassName());
        logStringBuilder.append("|");
        logStringBuilder.append(messageId);
        logStringBuilder.append("|");
        
        return logStringBuilder.toString();
    }
    
    /**
     * 
     * [简要描述]：</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-12)</br>
     *
     * @param payType 支付类型
     * @param billId 账单id
     * @param paymentId 支付id
     * @return 消息id
     *
     */
    protected String getMessageId(String payType, String billId, Integer paymentId)
    {
        // 作用：根据messageId可以快速的跟踪一次支付从开始到回调的全过程，方便定位问题
        
        // 如果还未生成paymentId
        if (null == paymentId)
        {
            // 规则：payType_billId
            return payType + PaymentConstant.UNDERLINE_SEPARATOR + billId;
        }
        
        // 规则: payType_billId_paymentId
        return payType + PaymentConstant.UNDERLINE_SEPARATOR + billId + PaymentConstant.UNDERLINE_SEPARATOR + paymentId;
    }
    
    /**
     * 
     * [简要描述]：分析结果，将结果赋值到billPayment对象</br>
     * [详细描述]：根据不同的支付渠道，做不同的解析</br>
     * [作者]：Aaron(2017-07-19)</br>
     *
     * @param billPayment 支付记录对象
     * @param paymentResponse 通知结果对象
     * @param helperObject 帮助对象
     * @param resultMap 返回的结果集
     * @return true继续执行后续操作  false结束流程返回结果
     * @throws LandseaException
     *
     */
    protected boolean analysisResult(BillPayment billPayment, Object paymentResponse, PaymentHelperBean helperObject, Map<String, Object> resultMap)
        throws LandseaException
    {
        // 根据支付渠道调用不同的生成方法
        String beanId = PaymentConstant.GENETRATE_BILLPAYMENT_PREFIX + BillPaymentCollections.getPayChannelByPayType(billPayment.getPayType());
        
        AbstractPaymentChannel paymentChannel = (AbstractPaymentChannel)appContext.getBean(beanId);
        
        String logInfo = getLog(billPayment, null);
        if (null == paymentChannel)
        {
            LOGGER.error(logInfo + "|get paymentChannel failed,beanId=" + beanId);
            throw LandseaException.createException(PaymentResultCode.E00150006);
        }
        
        // 收款时间
        billPayment.setTime(new Date());
        
        // 解析结果，将必要的字段封装到billPayment中
        boolean isNeedContinue = paymentChannel.analysisResult(billPayment, paymentResponse, helperObject, resultMap);
        
        return isNeedContinue;
    }
    
    /**
     * 
     * [简要描述]：支付本地业务</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-12)</br>
     *
     * @param payment
     * @throws LandseaException
     *
     */
    protected abstract void doLocalBusiness(BillPayment payment, PaymentHelperBean helperObject, Map<String, Object> resultMap)
        throws LandseaException;
    
    /**
     * 
     * [简要描述]：余额自动抵扣支付本地业务</br>
     * [详细描述]：逻辑和doLocalBusiness一致</br>
     * [作者]：Aaron(2017-07-12)</br>
     *
     * @param AotuPayBill
     * @param BillPayment
     * @throws LandseaException
     *
     */
    public abstract void aotoPayDoLocalBusiness(AotuPayBill aotuPayBill, BillPayment billPayment)
        throws LandseaException;
    
    /**
     * 
     * [简要描述]：获取处理类</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-07-12)</br>
     *
     * @return
     *
     */
    protected abstract String getProcessClassName();
    
    /**
     * 
     * [简要描述]：回调提交事务后处理</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-12-01)</br>
     *
     * @param object
     * @throws LandseaException
     *
     */
    public void afterRespCommitTranscationProcess(Map<String, Object> resultMap)
        throws LandseaException
    {
        String processKey = StringTools.getString(resultMap.get(PaymentConstant.AFTER_RESP_COMMIT_TRANSACTION));
        
        // 支付余额回调处理
        if (RechargePayResponseHandler.RECHARGE_RESPONSE.equals(processKey))
        {
            Integer accountId = (Integer)resultMap.get("accountId");
            // 处理自动抵扣，此处实时发送消息
            autoPayBillService.autoPay(accountId, null, true);
        }
    }
}
